﻿using Application.Service;
using Infrastructure.Extension;
using Infrastructure.Utils;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Polly;
using Quartz;
using System.Diagnostics;
using Neoit.Utils.TemplateParser;

namespace Application.Job
{
    public class TaskJob : IJob
    {
        private readonly JobManagerDbContext _dbContext;
        private readonly HttpClient _httpClient;
        private readonly Random _random = new Random();
        private bool _isNowRunOnce = false;
        private IJobService _jobService { get; set; }
        public TaskJob(JobManagerDbContext dbContext, IHttpClientFactory httpClientFactory, IJobService service)
        {
            _dbContext = dbContext;
            _httpClient = httpClientFactory.CreateClient();
            _jobService = service;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            var now = DateTime.Now;
            var dataMap = context.Trigger.JobDataMap;
            _isNowRunOnce = dataMap.GetBoolean("isNowRunOnce");
            var preTaskRecordId = dataMap.GetInt("preTaskRecordId");
            int dataId;
            if (_isNowRunOnce)
            {
                dataId = dataMap.GetInt("dataId");
            }
            else
            {
                dataId = Convert.ToInt32(context.JobDetail.Key.Name);
            }
            var data = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == dataId);
            if (data == null) { return; }
            var isSuccess = false;
            var result = string.Empty;
            var response = string.Empty;
            try
            {
                var _data = data.DeepCopy();
                if (preTaskRecordId > 0) await handlePreParam(_data, preTaskRecordId);
                switch (data.JobType)
                {
                    case JobType.Url:
                        (isSuccess, result,response) = await Request(_data);
                        break;
                    case JobType.Script:
                        (isSuccess, result, response) = ExecuteScrrpt(_data);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                isSuccess = false;
                result = ex.Combine();
            }
            result = result.Omit(490);
            #region 更新结果
            data.LastExecutionTime = now;
            data.LastExecutionStatus = isSuccess;
            data.Result = result;
            data.ExecutionCount = data.ExecutionCount + 1;
            var executionStatus = data.RecentExecutionStatus + (isSuccess ? "1" : "0");
            data.RecentExecutionStatus = executionStatus.Length > 10 ? executionStatus.Substring(executionStatus.Length - 10) : executionStatus;
            if (data.Cron.Has()) data.NextExecutionTime = SchedulerHelper.NextExecutionTime(data.Cron);
            if (!isSuccess) data.LastFailureTime = now;
            else data.LastSuccessTime = now;
            #endregion

            #region 添加执行记录

            var jobContext = new 
            {
                response = response
            };

            var record = new JobTaskRecord
            {
                IsSuccess = isSuccess,
                JobId = dataId,
                Result = result,
                JobType = data.JobType,
                Content = data.Content,
                Note = $"serverId:{GlobalData.ServerId}",
                HasNotice = false,
                Context = jobContext.ToJson()
            };
            await _dbContext.JobTaskRecord.AddAsync(record);
            #endregion
            await _dbContext.SaveChangesAsync();

            #region 通知策略
            var needNotice = false;
            switch (data.NoticePolicy)
            {
                case NoticePolicy.OnlyException:
                    if (!isSuccess) needNotice = true;
                    break;
                case NoticePolicy.ExceptionOrRecovery:
                    var lastRecord = await _dbContext.JobTaskRecord.OrderByDescending(s => s.CreateTime).FirstOrDefaultAsync(s => s.JobId == data.Id);
                    var isRecovery = !lastRecord.IsSuccess && isSuccess;//结果是否为恢复（即上次失败，本次成功）
                    if (!isSuccess || isRecovery) needNotice = true;
                    break;
                case NoticePolicy.Never:
                    needNotice = false;
                    break;
                case NoticePolicy.All:
                    needNotice = true;
                    break;
                default:
                    needNotice = false;
                    break;
            }
            if (needNotice)
            {
                var noticeObjects = (data.NoticeObjects ?? "").Replace("；", ";").Replace(",", ";").Replace("，", ";").TrimEnd(';').Split(';').Distinct();//规范统一数据
                if (noticeObjects.Has())
                {
                    var notices = new List<Notice>();
                    foreach (var item in noticeObjects)
                    {
                        if (!item.Has()) continue;
                        var notice = new Notice
                        {
                            Title = data.Title,
                            CreateTime = now,
                            IsBusinessSuccess = isSuccess,
                            Content = $"组:{data.Group};Url:{data.Content};结果:{data.Result};".MaxLength(2000),
                            NoticeObject = item,
                            SourceId = record.Id
                        };
                        notices.Add(notice);
                    }
                    if (notices.Has())
                    {
                        _dbContext.AddRange(notices);
                        record.HasNotice = true;
                    }
                }
            }
            #endregion

            await _dbContext.SaveChangesAsync();

            // 触发下个任务
            if (data.NextJobId != null)
            {
                var nextJobId = Convert.ToInt32(data.NextJobId);
                var nextJob = await _dbContext.JobTask.FirstOrDefaultAsync(s => s.Id == nextJobId);
                //nextJob.PreJobParam= result;
                await _jobService.Once(new RunOnceRequest { Id = nextJobId,PreTaskRecordId= record.Id });
            }
            return;
        }

        private async Task handlePreParam(JobTask jobTask, int preTaskRecordId)
        {
            // preParam="{'title':'1','b':'2'}"
            // header= access_token:{{$.access_token}}
            // header= access_token:${title}
            var preTaskRecord = await _dbContext.JobTaskRecord.OrderByDescending(s => s.CreateTime).FirstOrDefaultAsync(s => s.Id == preTaskRecordId);
            if (preTaskRecord.Content.Has())
            {
                var data = JObject.Parse(preTaskRecord.Context.JsonValue("response"));

                try
                {
                    if (jobTask.RequestHeader.Has()) jobTask.RequestHeader = ParseManager.Parse(jobTask.RequestHeader, data);
                }
                catch (Exception e)
                {
                    
                }

                try
                {
                    if (jobTask.Content.Has()) jobTask.Content = ParseManager.Parse(jobTask.Content, data);
                }
                catch (Exception e)
                {

                }

            }
        }

        private bool IsJsonString(string input)
        {
            try
            {
                // 尝试将字符串解析为JSON对象或数组
                var obj = JToken.Parse(input);
                return true;
            }
            catch (JsonReaderException)
            {
                // 如果解析失败，说明不是有效的JSON字符串
                return false;
            }
        }


        private async Task<(bool isSuccess, string result, string response)> Request(JobTask data)
        {
            var isSuccess = false;
            var result = string.Empty;
            var response = string.Empty;

            try
            {
                var headers = HttpHelper.ConvertToHeaderDic(data.RequestHeader).Where(s => !s.Key.StartsWith(":")).Select(s => (s.Key, s.Value)).ToList();

                var retryWait = new TimeSpan[] { };
                if (data.FailureRetryPolicy.Has())
                {
                    retryWait = data.FailureRetryPolicy.Split(",").Where(s => s.Has()).Select(s => TimeSpan.FromSeconds(Convert.ToInt32(s))).ToArray();
                }
                //随机延迟
                if (!_isNowRunOnce && data.RandomDelay > 0) await Task.Delay(_random.Next(0, data.RandomDelay * 1000));

                // polly失败重试
                var retryPolicy = Policy
                    .Handle<Exception>()
                    .WaitAndRetryAsync(retryWait, (exception, timeSpan, retryCount, context) =>
                    {
                        var msg = $"错误：{exception.Combine()}，开始重试第{retryCount}次[{DateTime.Now}]..\r\n";
                        result += msg;
                    });
                var res = await retryPolicy.ExecuteAsync(async () =>
                {
                    return await _httpClient.Request(HttpHelper.GetHttpMethod(data.RequestMethond ?? "get"), data.Content, headers, data.RequestBody);
                });
                var responseStr = await res.Content.ReadAsStringAsync();
                response = responseStr;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, responseStr))
                    {
                        isSuccess = true;
                        result = responseStr;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { HttpStatusCode = res.StatusCode, 期望值 = data.Expected, 实际值 = responseStr }).ToJson();
                    }
                }
                else
                {
                    isSuccess = res.IsSuccessStatusCode && res.StatusCode == System.Net.HttpStatusCode.OK;
                    result = res.StatusCode.ToString();
                }
            }
            catch (Exception e)
            {
                result += e.Combine();
            }

            return (isSuccess, result, response);
        }
        // exec Scrrpt
        private (bool isSuccess, string result, string response) ExecuteScrrpt(JobTask data)
        {
            bool isSuccess = false;
            string result;
            string response=string.Empty;
            if (Environment.OSVersion.Platform == PlatformID.Unix)
            {
                var process = new Process();
                process.StartInfo.FileName = "bash"; // 使用bash来执行脚本
                var content = data.Content.Replace("\"", "\\\"");
                process.StartInfo.Arguments = $"-c \"{content}\"";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                string output = process.StandardOutput.ReadToEnd();
                output = output.TrimEnd('\r', '\n');
                string error = process.StandardError.ReadToEnd();

                process.WaitForExit();
                int exitCode = process.ExitCode;
                response = output;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, output))
                    {
                        isSuccess = true;
                        result = output;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { ExitCode = exitCode, 期望值 = data.Expected, 实际值 = output, error = error }).ToJson();
                    }
                }
                else
                {
                    isSuccess = exitCode == 0;
                    result = exitCode == 0 ? output : error;
                }
            }
            else if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                Process process = new Process();
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = "/c " + data.Content;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;

                process.Start();
                string output = process.StandardOutput.ReadToEnd();
                output = output.TrimEnd('\r', '\n');
                process.WaitForExit();

                int exitCode = process.ExitCode;
                response = output;
                if (data.Expected.Has())
                {
                    if (IsEligible(data.Expected, output))
                    {
                        isSuccess = true;
                        result = output;
                    }
                    else
                    {
                        isSuccess = false;
                        result = (new { ExitCode = exitCode, 期望值 = data.Expected, 实际值 = output }).ToJson();
                    }
                }
                else
                {
                    isSuccess = exitCode == 0;
                    result = output;
                }
            }
            else
            {
                result = $"::不支持的平台：{Environment.OSVersion.Platform.GetName()}";
            }
            return (isSuccess, result, response);



            //(bool isSuccess, string result) sfd(string expected, string output,string msg)
            //{
            //    if (output == data.Expected || output.IsMatch(data.Expected))
            //    {
            //        isSuccess = true;
            //        result = output;
            //    }
            //    else
            //    {
            //        isSuccess = false;
            //        result = (new { ExitCode = exitCode, 期望值 = expected, 实际值 = output, error = error }).ToJson();
            //    }
            //}

        }
        //符合期望
        bool IsEligible(string expected, string actual)
        {
            return actual == expected || actual.IsMatch($"^{expected}$");
        }
    }

    public class DeleteJobListener : IJobListener
    {
        public string Name => "DeleteJobListener";

        public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }

        public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }

        public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
        {
            IScheduler scheduler = context.Scheduler;
            await scheduler.DeleteJob(context.JobDetail.Key);
        }
    }

}
